#include <string.h>
#include <malloc.h>
#include "stdio.h"

// 定义结构体：
struct Student {
    // 成员不会自动初始化，未初始化就调用会造成意想不到的错误
    int id;           //学号
    char name[20];    //姓名
    char gender;      //性别
    char address[50]; //家庭住址
};

void testStruct() {
    struct Student stu1;
    stu1.id = 1002;
    //    stu1.name = "李四"; //报错，不能直接通过赋值运算符来给字符数组赋值
    strcpy(stu1.name, "李四");
    stu1.gender = 'F';
    strcpy(stu1.address, "上海市浦东新区");
    printf("姓名：%s\n", stu1.name);

    struct Student stu2 = {1001, "张三", 'M', "北京市海淀区"};
    stu2.id = 1002;
    strcpy(stu2.name, "王五");
    stu2.gender = 'F';
    strcpy(stu2.address, "上海市浦东新区");
    printf("姓名：%s\n", stu2.name);

    struct Student stu3 = {.id=1003, .name="赵六", .gender='M', "广州市天河区"};
    printf("姓名：%s\n", stu3.name);

    // 结构体指针
    struct Circle {
        int id;
        double radius;
    } c1;
    c1.id = 1;
    c1.radius = 1.0;
    printf("半径：%f\n", c1.radius);

    // 匿名结构体
    struct {
        char name[20];
        int age;
        char gender;
        char phone[11];
    } emp1, emp2;
    strcpy(emp1.name, "张三");
    emp1.age = 20;
    printf("姓名：%s\n", emp1.name);
}

// 别名
typedef struct cell_phone {
    int phone_no;              //电话号码
    double minutes_of_charge;  //每分钟费用
} Phone;  // Phone 是 cell_phone 的别名

void testTypeAlias() {
    Phone phone1;
    phone1.phone_no = 1001;
    phone1.minutes_of_charge = 0.5;
    printf("电话号码：%d\n", phone1.phone_no);

    Phone phone2 = {1002, 0.6};
    printf("电话号码：%d\n", phone2.phone_no);
}

//声明匿名结构体
typedef struct {
    int phone_no;
    double minutes_of_charge;
} telephone;

void test_anonymous_struct() {
    telephone tel1;
    tel1.phone_no = 1001;
    tel1.minutes_of_charge = 0.5;
    printf("电话号码：%d\n", tel1.phone_no);
}

// 嵌套
struct Name {
    char firstName[50];
    char lastName[50];
};
struct Student2 {
    int id;
    char gender;
    struct Name name;
};

void test_nesting() {
    struct Student2 stu1;
    stu1.id = 1001;
    stu1.gender = 'M';
    strcpy(stu1.name.firstName, "张");
    strcpy(stu1.name.lastName, "三");
    printf("姓名：%s%s\n", stu1.name.firstName, stu1.name.lastName);

    struct Student2 stu2;
    stu2.id = 1002;
    stu2.gender = 'F';
    struct Name name = {"王", "五"};
    stu2.name = name;
    printf("姓名：%s%s\n", stu2.name.firstName, stu2.name.lastName);

    struct Student2 stu3 = {1003, 'M', {"赵", "六"}};
    printf("姓名：%s%s\n", stu3.name.firstName, stu3.name.lastName);
}

/*
 * 结构体占用的存储空间，不是各个属性存储空间的总和。
 * 为了计算效率，C 语言的内存占用空间一般来说，都必须是 int 类型存储空间的整数倍。
 * 如果 int 类型的存储是4字节，那么 struct 类型的存储空间就总是4的倍数。
 *
 */
struct A {
    char a;
    short b;
    int c;
};
struct B {
    char a;
    char a1;
    char a2;
    short b;
    int c;
    short b1;
};

// 计算结构体大小： 从第一个属性开始，若 属性 size < 4byte,则与下一个属性size相加，若相加后size < 4byte,则继续与下一个属性相加，
// 相加后size = 4byte,则size = 4byte; 相加后size > 4byte，则 size = 4byte * 2 。
void test_struct_size() {
    printf("short size:%d\n", sizeof(short));
    printf("char size:%d\n", sizeof(char));
    printf("int size:%d\n", sizeof(int));

    printf("struct A size:%d\n", sizeof(struct A)); // 8
    printf("struct B size:%d\n", sizeof(struct B)); // 12
}

/*
 * 当使用赋值操作（=）或memcpy复制结构体时，所有成员（包括数组）会逐字节完整拷贝到新结构体中，属于深拷贝。
 * 特别 ,指针 复制的是 指针变量存储的指针地址，也可以认为是值拷贝。
 */
struct C {
    char name[20];
    int age;
    char gender;
    char phone[11];
};

void test_struct_copy() {
    struct C c1 = {"张三", 20, 'M', "12345678901"};
    struct C c2 = c1;
    printf("c1姓名：%s,地址 ： %p\n", c1.name, &c1.name);
    printf("c2姓名：%s,地址 ： %p\n", c2.name, &c2.name);
    strcpy(c2.name, "王五22");
    printf("c1姓名：%s,地址 ： %p\n", c1.name, &c1.name);
    printf("c2姓名：%s,地址 ： %p\n", c2.name, &c2.name);
}

// 结构体数组
struct Person {
    char name[20];
    int age;
};

void test_struct_array() {
    struct Person p[3] = {{"张三", 20},
                          {"李四", 30},
                          {"王五", 40}};
    printf("姓名：%s\n", p[1].name);

    struct Person2 {
        char name[20];
        int age;
    } p2[3] = {{"张三", 20},
               {"李四", 30},
               {"王五", 40}};
    printf("姓名：%s\n", p2[0].name);
}

// 结构体指针
void addAge(struct Person *per) {   //说明1
    // 第一种写法
//    (*per).age = (*per).age + 1;    //说明2
    // 第二种写法
    per->age = per->age + 1;    //说明3
}

void test_struct_Pointer() {
    struct Person p = {"张三", 20};
    struct Person *p1 = &p;
    printf("姓名：%s,年龄 ： %d\n", p1->name, p1->age);
    addAge(p1);
    printf("姓名：%s,年龄 ： %d\n", p1->name, p1->age);
}

void test_struct_array_pointer() {
    struct Person p[3] = {{"张三", 20},
                          {"李四", 30},
                          {"王五", 40}};

    for (int i = 0; i < 3; i++) {
        printf("姓名：%s,年龄 ： %d\n", p[i].name, p[i].age);
    }
    printf("------------------------\n");

    struct Person *p1 = p;
    for (int i = 0; i < 3; i++) {
        printf("姓名：%s,年龄 ： %d\n", p1[i].name, p1[i].age);
    }
    printf("------------------------\n");
    for (int i = 0; i < 3; i++) {
        printf("姓名：%s,年龄 ： %d\n", (p1 + i)->name, (p1 + i)->age);
    }
    printf("------------------------\n");
    // 不要使用方式遍历，因为会移动指针，
    for (int i = 0; i < 3; i++, p1++) {
        printf("姓名：%s,年龄 ： %d\n", (*p1).name, (*p1).age);
    }
    printf("------------------------\n");
    p1 = p; // 上面的遍历移动了指针，所以需要复位
    // 不要使用方式遍历，因为会移动指针，
    for (int i = 0; i < 3; i++, p1++) {
        printf("姓名：%s,年龄 ： %d\n", p1->name, p1->age);
    }
}

// malloc动态分配内存;free释放内存
void test_malloc() {
    struct Person *p = NULL;
    p = (struct Person *) malloc(sizeof(struct Person));
    strcpy(p->name, "张三");
    p->age = 20;
    printf("姓名：%s,年龄 ： %d\n", p->name, p->age);
    // 使用malloc分配的内存，一定要释放
    free(p);
}

/**
 * 共用体
 * 共用体是一种特殊的数据类型，允许您在相同的内存位置存储不同的数据类型。
 * 也就是说，您可以定义一个数据类型，它可以在同一内存位置存储多种类型的数据。
 * 共用体提供了一种使用相同的内存位置的有效方式。
 *
 * 1 .size : 由数据类型中存储最宽的数据类型决定。
 * 2. 属性有效性 ：只能有一个属性处于活动状态，其他属性会被覆盖忽略。
 * 除以上2点，其他和 struct 一样。
 *
 */
union Data {
    long m;
    float x;
    char c;
};

void test_union_create() {
    //声明共用体变量
    union Data a, b;

    union Data2 {
        short m;
        float x;
        char c;
    } c, d;

    union {
        short m;
        float x;
        char c;
    } e, f;
}

void test_union_use() {
    union Data a;
    //最后赋值属性会被保存，先前赋值的属性会被覆盖
    a.m = 2; // a 现在包含整数 10
    a.x = 20.1F; // 现在 a 包含 220.5
    a.c = 'A'; // 现在 a 包含字符 A
    printf("m 的值被覆盖 ： %d\n", a.m); // 错误的值
    printf("x 的值被覆盖 ： %f\n", a.x); // 错误的值
    printf("c 的值 ： %c\n", a.c); // 正确的值
    printf("a 的数据长度 ： %lu\n", sizeof(a));
}

// 类型定义
void test_typedef() {
    typedef int Integer;
    Integer a = 10;
    printf("a 的值 ： %d\n", a);
    typedef struct Person p;
    p p1 = {"张三", 20};
    printf("姓名：%s,年龄 ： %d\n", p1.name, p1.age);
}

int main() {
//    testStruct();
//    testTypeAlias();
//    test_anonymous_struct();
//    test_nesting();
//    test_struct_size();
//    test_struct_copy();
//    test_struct_array();
//    test_struct_Pointer();
//    test_struct_array_pointer();
//    test_malloc();
//    test_union_create();
//    test_union_use();
//    test_typedef();
}